UNPKG

studiocms

Version:

Astro Native CMS for AstroDB. Built from the ground up by the Astro community.

232 lines (178 loc) 5.97 kB
import { apiResponseLogger } from 'studiocms:logger'; import { Notifications } from 'studiocms:notifier'; import { SDKCore } from 'studiocms:sdk'; import type { tsPageContentSelect, tsPageDataSelect } from 'studiocms:sdk/types'; import { AllResponse, createEffectAPIRoutes, createJsonResponse, Effect, genLogger, OptionsResponse, readAPIContextJson, } from '../../../../../../effect.js'; import { verifyAuthTokenFromHeader } from '../../../utils/auth-token.js'; type UpdatePageData = Partial<tsPageDataSelect>; type UpdatePageContent = Partial<tsPageContentSelect>; interface UpdatePageJson { pageId: string; data?: UpdatePageData; content?: UpdatePageContent; } export const { GET, PATCH, DELETE, OPTIONS, ALL } = createEffectAPIRoutes( { GET: (ctx) => genLogger('studioCMS:rest:v1:pages:[id]:GET')(function* () { const [sdk, user] = yield* Effect.all([SDKCore, verifyAuthTokenFromHeader(ctx)]); if (user instanceof Response) { return user; } const { rank } = user; if (rank !== 'owner' && rank !== 'admin' && rank !== 'editor') { return apiResponseLogger(401, 'Unauthorized'); } const { id } = ctx.params; if (!id) { return apiResponseLogger(400, 'Invalid page ID'); } const page = yield* sdk.GET.page.byId(id); if (!page) { return apiResponseLogger(404, 'Page not found'); } return createJsonResponse(page); }), PATCH: (ctx) => genLogger('studioCMS:rest:v1:pages:[id]:PATCH')(function* () { const [sdk, user, notifier] = yield* Effect.all([ SDKCore, verifyAuthTokenFromHeader(ctx), Notifications, ]); if (user instanceof Response) { return user; } const { rank, userId } = user; if (rank !== 'owner' && rank !== 'admin' && rank !== 'editor') { return apiResponseLogger(401, 'Unauthorized'); } const { id } = ctx.params; if (!id) { return apiResponseLogger(400, 'Invalid page ID'); } const jsonData = yield* readAPIContextJson<UpdatePageJson>(ctx); const { data, content } = jsonData; if (!data) { return apiResponseLogger(400, 'Invalid form data, data is required'); } if (!content) { return apiResponseLogger(400, 'Invalid form data, content is required'); } if (!data.id) { return apiResponseLogger(400, 'Invalid form data, id is required'); } if (!content.id) { return apiResponseLogger(400, 'Invalid form data, id is required'); } if (data.id !== id) { return apiResponseLogger(400, 'Payload id does not match path id'); } const currentPageData = yield* sdk.GET.page.byId(id); if (!currentPageData) { return apiResponseLogger(404, 'Page not found'); } const { authorId, contributorIds, defaultContent } = currentPageData.data; let AuthorId = authorId; if (!authorId) { AuthorId = userId || null; } const ContributorIds = contributorIds || []; if (!ContributorIds.includes(userId)) { ContributorIds.push(userId); } data.authorId = AuthorId; data.contributorIds = ContributorIds; data.updatedAt = new Date(); const startMetaData = (yield* sdk.GET.databaseTable.pageData()).find( (metaData) => metaData.id === data.id ); yield* sdk.UPDATE.page.byId(data.id, { pageData: data as tsPageDataSelect, pageContent: content as tsPageContentSelect, }); const updatedMetaData = (yield* sdk.GET.databaseTable.pageData()).find( (metaData) => metaData.id === data.id ); const siteConfig = yield* sdk.GET.siteConfig(); if (!siteConfig) { return apiResponseLogger(500, 'Site configuration not found'); } const { enableDiffs, diffPerPage = 10 } = siteConfig.data; if (enableDiffs) { yield* sdk.diffTracking.insert( userId, data.id, { content: { start: defaultContent?.content || '', end: content.content || '', }, // biome-ignore lint/style/noNonNullAssertion: This is a valid use case for non-null assertion metaData: { start: startMetaData!, end: updatedMetaData! }, }, diffPerPage ); } yield* sdk.CLEAR.page.byId(id); // biome-ignore lint/style/noNonNullAssertion: This is a valid use case for non-null assertion yield* notifier.sendEditorNotification('page_updated', updatedMetaData!.title); return apiResponseLogger(200, 'Page updated successfully'); }).pipe(Notifications.Provide), DELETE: (ctx) => genLogger('studioCMS:rest:v1:pages:[id]:DELETE')(function* () { const [sdk, user, notifier] = yield* Effect.all([ SDKCore, verifyAuthTokenFromHeader(ctx), Notifications, ]); if (user instanceof Response) { return user; } const { rank } = user; if (rank !== 'owner' && rank !== 'admin') { return apiResponseLogger(401, 'Unauthorized'); } const { id } = ctx.params; if (!id) { return apiResponseLogger(400, 'Invalid page ID'); } const jsonData = yield* readAPIContextJson<{ slug: string }>(ctx); const { slug } = jsonData; if (!slug) { return apiResponseLogger(400, 'Invalid request'); } const page = yield* sdk.GET.page.byId(id); if (!page) { return apiResponseLogger(404, 'Page not found'); } yield* sdk.DELETE.page(id); yield* sdk.CLEAR.page.byId(id); yield* notifier.sendEditorNotification('page_deleted', page.data.title); return apiResponseLogger(200, 'Page deleted successfully'); }).pipe(Notifications.Provide), OPTIONS: () => Effect.try(() => OptionsResponse({ allowedMethods: ['GET', 'PATCH', 'DELETE'] })), ALL: () => Effect.try(() => AllResponse()), }, { cors: { methods: ['GET', 'PATCH', 'DELETE', 'OPTIONS'] }, onError: (error) => { console.error('API Error:', error); return createJsonResponse( { error: 'Internal Server Error' }, { status: 500, } ); }, } );